package com.quectel.business.dms.application.access.accessor.im.impl;

import cn.hutool.core.lang.func.LambdaUtil;
import com.google.common.base.Preconditions;
import com.quectel.business.dms.application.ApplicationAccessConf;
import com.quectel.business.dms.application.access.accessor.im.AbstractIntelligentMonitorAccessor;
import com.quectel.business.dms.application.access.accessor.im.model.PlaybackPart;
import com.quectel.business.dms.application.access.accessor.im.model.SearchMapReq;
import com.quectel.business.dms.application.access.accessor.im.model.SearchMapResp;
import com.quectel.business.dms.application.access.accessor.im.model.StreamResp;
import com.quectel.business.dms.application.access.basemodel.ApplicationOriginDevice;
import com.quectel.business.dms.device.DeviceBusiness;
import com.quectel.constant.core.device.DeviceConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.module.application.dto.ApplicationDto;
import com.quectel.core.module.application.service.ApplicationService;
import com.quectel.core.module.device.dto.DeviceCommandTemplateDto;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.device.service.DeviceTypeService;
import com.quectel.sdk.dahua9100.Dahua9100ClientBuilder;
import com.quectel.sdk.dahua9100.monitor.MonitorClient;
import com.quectel.sdk.dahua9100.monitor.model.DeviceResp;
import com.quectel.sdk.dahua9100.monitor.model.SnapFaceRecordResp;
import com.quectel.sdk.dahua9100.monitor.model.VideoPlaybackResp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author rananxin
 * @date 2021/12/1 9:37 上午
 */
@Component
@Slf4j
public class Dahua9100MonitorAccessorImpl extends AbstractIntelligentMonitorAccessor {

    @DubboReference
    private DeviceTypeService deviceTypeService;
    @DubboReference
    private DeviceService deviceService;
    @DubboReference
    private ApplicationService applicationService;
    @Autowired
    private DeviceBusiness deviceBusiness;

    @Override
    public boolean initApplication(ApplicationDto application) {
        return syncOrRegisterDevices(application, null);
    }


    @Override
    public boolean syncOrRegisterDevices(ApplicationDto application, List<ApplicationOriginDevice> devices) {
        final Date currentDate = new Date();

        ApplicationAccessConf.TemplateOfDahua9100Conf templateOfDahua9100Conf = ApplicationAccessConf
                .parseExtStr(ApplicationAccessConf.TemplateOfDahua9100Conf.class, application.getAccessConf());
        try {
            assert templateOfDahua9100Conf != null;
            MonitorClient monitorInstance = Dahua9100ClientBuilder.builder()
                    .username(templateOfDahua9100Conf.getUsername())
                    .password(templateOfDahua9100Conf.getPassword())
                    .ip(templateOfDahua9100Conf.getIp())
                    .build(MonitorClient.class);

            List<DeviceResp.ResultsCls> resultsCls = monitorInstance.drawResources();

            List<DeviceDto> deviceDtos = new ArrayList<>();
            for (DeviceResp.ResultsCls resultsCl : resultsCls) {
                DeviceDto deviceDto = deviceService.selectByUniqueId(resultsCl.getId(), application.getTenantId());
                if (deviceDto == null) {
                    deviceDto = new DeviceDto();
                    deviceDto.setUniqueId(resultsCl.getId());
                    deviceDto.setImei(resultsCl.getId());
                    deviceDto.setName(resultsCl.getName());
                    deviceDto.setCreateTime(currentDate);

                    fill(deviceDto, application);
                    deviceDtos.add(deviceDto);
                } else {
                    DeviceDto newlyDto = new DeviceDto();
                    newlyDto.setId(deviceDto.getId());
                    newlyDto.setOnlineStatus((byte) ("1".equals(resultsCl.getOnline()) ? 1 : 0));
                    deviceService.updateById(newlyDto);
                }
            }

            deviceService.saveBatch(deviceDtos);
            return true;
        } catch (Exception e) {
            log.error("大华9100 监控同步 syncOrRegisterDevices 异常", e);
        }
        return false;
    }

    @Override
    public boolean subscribeEvents(ApplicationDto application) {
        // 大华9100事件记录通过轮询抓取

        return true;
    }

    @Override
    public boolean sendCmd(DeviceDto deviceDto, DeviceCommandTemplateDto deviceCommandTemplateDto, Map<String, Object> params, Long cmdId) {
        return false;
    }


    @Override
    public List<StreamResp> getStreamUrls(List<DeviceDto> monitors) {
        Map<Long, MonitorClient> instanceMap = new HashMap<>();

        List<String> streams = new ArrayList<>();
        for (DeviceDto monitor : monitors) {
            Long applicationId = monitor.getApplicationId();
            String url = "";
            try {
                MonitorClient monitorInstance = instanceMap.computeIfAbsent(applicationId, aLong -> {
                    ApplicationDto applicationDto = applicationService.selectById(aLong);
                    ApplicationAccessConf.TemplateOfDahua9100Conf templateOfDahua9100Conf = ApplicationAccessConf
                            .parseExtStr(ApplicationAccessConf.TemplateOfDahua9100Conf.class, applicationDto.getAccessConf());

                    assert templateOfDahua9100Conf != null;
                    return Dahua9100ClientBuilder.builder()
                            .username(templateOfDahua9100Conf.getUsername())
                            .password(templateOfDahua9100Conf.getPassword())
                            .ip(templateOfDahua9100Conf.getIp())
                            .build(MonitorClient.class);
                });
                List<String> previewDefaultUrls = monitorInstance.previewDefaultUrl(Collections.singletonList(monitor.getUniqueId()));
                url = previewDefaultUrls.get(0);
            } catch (Exception e) {
                log.error("大华9100 监控对接 getStreamUrls 异常", e);
            }
            streams.add(url);
        }
        return streams.stream().map(v -> {
            if (v == null) {
                return null;
            }
            return new StreamResp(v, StreamResp.StreamType.HLS264);
        }).collect(Collectors.toList());
    }

    @Override
    public List<PlaybackPart> getPlaybackParts(DeviceDto monitor, Date startTime, Date endTime) {
        ApplicationDto applicationDto = applicationService.selectById(monitor.getApplicationId());
        if (applicationDto == null) {
            return Lists.newArrayList();
        }

        ApplicationAccessConf.TemplateOfDahua9100Conf templateOfDahua9100Conf = ApplicationAccessConf
                .parseExtStr(ApplicationAccessConf.TemplateOfDahua9100Conf.class, applicationDto.getAccessConf());

        assert templateOfDahua9100Conf != null;
        MonitorClient build = Dahua9100ClientBuilder.builder()
                .username(templateOfDahua9100Conf.getUsername())
                .password(templateOfDahua9100Conf.getPassword())
                .ip(templateOfDahua9100Conf.getIp())
                .build(MonitorClient.class);

        List<VideoPlaybackResp> playbackResps = build.defaultPlayback(monitor.getUniqueId(), startTime, endTime);
        return playbackResps.parallelStream().map(o -> {
            PlaybackPart playbackPart = new PlaybackPart();
            playbackPart.setStartTime(o.getStartTime());
            playbackPart.setEndTime(o.getEndTime());
            playbackPart.setStreamUrl(o.getUrl());
            return playbackPart;
        }).collect(Collectors.toList());
    }

    @Override
    public SearchMapResp searchMap(SearchMapReq req) {
        ApplicationDto application = req.getApplicationDto();
        Preconditions.checkNotNull(application);

        ApplicationAccessConf.TemplateOfDahua9100Conf templateOfDahua9100Conf = ApplicationAccessConf
                .parseExtStr(ApplicationAccessConf.TemplateOfDahua9100Conf.class, application.getAccessConf());

        assert templateOfDahua9100Conf != null;
        MonitorClient build = Dahua9100ClientBuilder.builder()
                .username(templateOfDahua9100Conf.getUsername())
                .password(templateOfDahua9100Conf.getPassword())
                .ip(templateOfDahua9100Conf.getIp())
                .build(MonitorClient.class);

        Map<String, Object> seekFitDeviceParams = new HashMap<>();
        seekFitDeviceParams.put(LambdaUtil.getFieldName(DeviceDto::getApplication), application.getId());
        seekFitDeviceParams.put(DeviceConstants.QueryParamEnum.NOT_FREE.getParamKey(), SystemConstants.NORMAL.toString());
        seekFitDeviceParams.put(LambdaUtil.getFieldName(DeviceDto::getDeviceTypeId), application.getDeviceTypeId());
        List<DeviceDto> deviceDtoList = deviceService.queryList(seekFitDeviceParams);


        List<SnapFaceRecordResp.ResultsCls> resultsCls = build.searchMap(req.getFaceImg(), req.getSimilarity()
                , deviceDtoList.parallelStream()
                        .map(DeviceDto::getUniqueId).collect(Collectors.toList())
                , req.getStartTime(), req.getEndTime(), req.getPageNo(), req.getPageSize());

        List<SearchMapResp.SearchMapItem> result = resultsCls.stream().map(o -> {
            DeviceDto deviceDto = deviceService.selectCacheByUniqueId(o.getChannelId(), application.getTenantId());
            if (deviceDto == null) {
                return null;
            }
            SearchMapResp.SearchMapItem searchMapItem = new SearchMapResp.SearchMapItem();
            searchMapItem.setUniqueId(o.getChannelId());
            searchMapItem.setRecordTime(o.getCapTime());
            if (o.getSimilarity() != null) {
                searchMapItem.setSimilarity(new BigDecimal(o.getSimilarity().toString()).multiply(new BigDecimal("100")));
            }
            searchMapItem.setFaceImg(o.getFaceImgUrl());
            searchMapItem.setDevice(deviceDto);
            return searchMapItem;
        }).filter(Objects::nonNull).collect(Collectors.toList());

        return new SearchMapResp(result.size(), result);
    }
}
